﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reactive;
using System.Reactive.Linq;
using System.Reactive.Subjects;

/// <summary>
/// Implement a simple self-contained "chat" client.
/// </summary>
/// <remarks>
/// I'd seen this suggested as a Kata to familiarize someone with Go or Erlang channels, and thought
/// it would be equally applicable to Rx Observables, as those are roughly equivalent.
/// 
/// Seemed like a good opportunity to play with Rx again, as my knowledge had grown pretty rusty.
/// </remarks>
namespace RandomKatas.ChatKata
{
    public class Chatter
    {
        public static void Chat()
        {
            var queue = new Subject<Tuple<string, string>>();
            var users = new Dictionary<string, User>();
            try
            {
                while (true)
                {
                    Console.Write("user:msg or 'quit' to exit> ");
                    var line = Console.ReadLine().Trim();
                    if (line.Equals("quit")) break;
                    var split = line.Split(null, 2);
                    var userName = split[0].TrimEnd(':');
                    var msg = split[1];

                    User user;
                    if (!users.TryGetValue(userName, out user))
                    {
                        user = new User(userName, queue);
                        Console.WriteLine("Creating user {0}", userName);
                        users[userName] = user;
                    }
                    user.Chat(msg);
                }
            }
            finally
            {
                queue.Dispose();
                users.Values.ToList().ForEach(u => u.Dispose());
            }
        }
    }

    public class User : IDisposable
    {
        public User(string name, Subject<Tuple<string, string>> queue)
        {
            this.Name = name;
            this.queue = queue;
            this.subscription = this.queue.Subscribe(msg =>
            {
                if (msg.Item1 != this.Name)
                    Console.WriteLine("{0} hears {1} say {2}", this.Name, msg.Item1, msg.Item2);
            });
        }

        public string Name { get; private set; }

        public void Chat(string msg)
        {
            queue.OnNext(Tuple.Create(this.Name, msg));
        }

        public void Dispose()
        {
            if (this.subscription != null)
                this.subscription.Dispose();
            this.subscription = null;
        }

        private Subject<Tuple<string, string>> queue;
        private IDisposable subscription;
    }
}
